﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;

namespace Noris.Schedule.Support.Services
{
	// DEKLARACE interface a args PRO SLUŽBY TYPU "FUNKCE"
	#region INTERFACE IFunctionMenuItem : předpis pro položku menu, které se nabízí na elementu grafu
	/// <summary>
	/// Interface, který předepisuje vlastnosti programu, který vytváří položku menu, které se nabízí na elementu grafu.
	/// </summary>
	public interface IFunctionMenuItem : IPlugin
	{
		/// <summary>
		/// Systém se ptá poskytovatele, zda chce pro daný element nabídnout svoji funkci.
		/// Tato metoda se volá jen při prvním zobrazování menu.
		/// Poskytovatel se rozhodne podle typu grafu, typu elementu a třídy řádku a elementu.
		/// V tuto chvíli není znám konkrétní element, rozhodnutí platí pro všechny obdobné elementy grafu.
		/// Pro danou kombinaci se systém ptá jen jednou.
		/// Pro konkrétní řádek a element lze pouze řídit Enabled/Disabled položky menu, a to metodou IsMenuItemEnabledFor().
		/// Pro aktivaci používání metody IsMenuItemEnabledFor() je třeba zde nastavit args.EnabledStateDependingOnElement = true;.
		/// </summary>
		/// <param name="args">Specifikace situace, která nastala, a pro kterou systém hledá nabídku funkcí. 
		/// Do tohoto argumentu by měla tato metoda vepsat popisek menu, pokud jej chce nabízet.</param>
		/// <returns></returns>
		bool IsFunctionSuitableFor(FunctionMenuItemSuitableArgs args);
		/// <summary>
		/// Metoda je volaná před každým zobrazením menu, a to jen pro providery kteří o to požádali.
		/// Tato žádost se vyjadřuje nastavením args.EnabledStateDependingOnElement = true; v metodě IsFunctionSuitableFor().
		/// Metoda vrací true, pokud se její menu má zobrazit jako Enabled.
		/// V tuto chvíli jsou přesně známy údaje o místě kliknutí, o konkrétním elementu i o dalších vybraných elementech.
		/// </summary>
		/// <param name="args"></param>
		/// <returns></returns>
		bool IsMenuItemEnabledFor(FunctionMenuItemRunArgs args);
		/// <summary>
		/// Vstupní metoda do výkonné části pluginu
		/// </summary>
		/// <param name="args"></param>
		void Run(FunctionMenuItemRunArgs args);
	}
	#endregion
	#region CLASS FunctionMenuItemKey : SPECIFIKACE MENU (typ grafu, typ prostoru, typ elementu, datová třída)
	/// <summary>
	/// Třída popisuje příležitost, pro kterou se hledají vhodná menu.
	/// Pokud konkrétní provider chce v dané situaci nabídnout svoje menu, pak nastaví jeho vlastnosti a vrátí true.
	/// </summary>
	public struct FunctionMenuItemKey
	{
		#region KONSTRUKCE
		/// <summary>
		/// Konstruktor
		/// </summary>
		/// <param name="graphMode">Typ grafu</param>
		/// <param name="menuKind">Prostor, kde se kliklo</param>
		/// <param name="elementDrawType">Druh elementu</param>
		/// <param name="rowClassNumber">Číslo třídy řádku</param>
		/// <param name="elementClassNumber">Číslo třídy elementu</param>
		public FunctionMenuItemKey(RowGraphMode graphMode, FunctionMenuItemAreaType menuKind, GraphElementShapeType elementShapeType, int rowClassNumber, int elementClassNumber)
		{
			this._GraphMode = graphMode;
			this._AreaType = menuKind;
			this._ElementShapeType = elementShapeType;
			this._RowClassNumber = rowClassNumber;
			this._ElementClassNumber = elementClassNumber;
		}
		#endregion
		#region PROMĚNNÉ
		private RowGraphMode _GraphMode;
		private FunctionMenuItemAreaType _AreaType;
		private GraphElementShapeType _ElementShapeType;
		private int _RowClassNumber;
		private int _ElementClassNumber;
		#endregion
		#region OVERRIDES
		public override string ToString()
		{
			return "{RowGraph=" + this.GraphMode + "; RowClass=" + this.RowClassNumber + "; AreaType=" + this.AreaType + "; ElementDraw=" + this.ElementShapeType + "}";
		}
		public override int GetHashCode()
		{
			return this.GraphModeInt ^ this.AreaTypeInt ^ this.ElementShapeTypeInt ^ this.RowClassNumber ^ this.ElementClassNumber;
		}
		public override bool Equals(object obj)
		{
			if (obj is FunctionMenuItemKey)
			{
				FunctionMenuItemKey other = (FunctionMenuItemKey)obj;
				return (this._GraphMode == other._GraphMode &&
					this._AreaType == other._AreaType &&
					this._ElementShapeType == other._ElementShapeType &&
					this._RowClassNumber == other._RowClassNumber &&
					this._ElementClassNumber == other._ElementClassNumber);
			}
			return false;
		}
		private int GraphModeInt { get { return (int)this._GraphMode; } }
		private int AreaTypeInt { get { return (int)this._AreaType; } }
		private int ElementShapeTypeInt { get { return (int)this._ElementShapeType; } }
		#endregion
		#region PROPERTY
		/// <summary> { get; }
		/// Typ grafu v aktuálním řádku
		/// </summary>
		public RowGraphMode GraphMode { get { return this._GraphMode; } }
		/// <summary> { get; }
		/// Typ prostoru, kam uživatel kliknul
		/// </summary>
		public FunctionMenuItemAreaType AreaType { get { return this._AreaType; } }
		/// <summary> { get; }
		/// Číslo třídy řádku
		/// </summary>
		public int RowClassNumber { get { return this._RowClassNumber; } }
		/// <summary> { get; }
		/// Číslo třídy elementu
		/// </summary>
		public int ElementClassNumber { get { return this._ElementClassNumber; } }
		/// <summary> { get; }
		/// Tvar elementu.
		/// </summary>
		public GraphElementShapeType ElementShapeType { get { return this._ElementShapeType; } }
		#endregion
	}
	#endregion
	#region CLASS FunctionMenuItemSuitableArgs : ZÍSKÁNÍ INFORMACÍ O MENU: URČENÍ TEXTU MENU A ZATŘÍDĚNÍ DO SKUPINY
	/// <summary>
	/// FunctionMenuItemSuitableArgs : Třída, pomocí které se provider vyjadřuje ke konkrétní situaci, zda chce nabízet svoje menu.
	/// </summary>
	public class FunctionMenuItemSuitableArgs
	{
		#region KONSTRUKCE
		public FunctionMenuItemSuitableArgs(FunctionMenuItemKey menuKey)
		{
			this._MenuKey = menuKey;
		}
		#endregion
		#region PROPERTY
		/// <summary> { get; }
		/// Typ grafu v aktuálním řádku
		/// </summary>
		public RowGraphMode KeyGraphMode { get { return this._MenuKey.GraphMode; } }
		/// <summary> { get; }
		/// Typ prostoru, kam uživatel kliknul
		/// </summary>
		public FunctionMenuItemAreaType KeyAreaType { get { return this._MenuKey.AreaType; } }
		/// <summary> { get; }
		/// Číslo třídy řádku
		/// </summary>
		public int KeyRowClassNumber { get { return this._MenuKey.RowClassNumber; } }
		/// <summary> { get; }
		/// Číslo třídy elementu
		/// </summary>
		public int KeyElementClassNumber { get { return this._MenuKey.ElementClassNumber; } }
		/// <summary> { get; }
		/// Tvar elementu.
		/// </summary>
		public GraphElementShapeType KeyElementShapeType { get { return this._MenuKey.ElementShapeType; } }
		/// <summary> { get; set; }
		/// Kompletně sestavená položka menu.
		/// Pokud funkce chce, může jí sestavit včetně všech parametrů, a takto bude položka zobrazována.
		/// </summary>
		public ToolStripItem MenuItem { get; set; }
		/// <summary> { get; set; }
		/// Text menu
		/// </summary>
		public string MenuCaption { get; set; }
		/// <summary> { get; set; }
		/// Obrázek v menu
		/// </summary>
		public Image MenuImage { get; set; }
		/// <summary> { get; set; }
		/// Text ToolTipu
		/// </summary>
		public string MenuToolTipText { get; set; }
		/// <summary> { get; set; }
		/// Příznak, že položka menu může měnit svůj stav podle stavu elementu, na kterém je menu zavoláno.
		/// Pokud ano, pak před zobrazením menu je volána metoda IFunctionMenuItem.GetMenuItemEnabledFor() s předáním informací o aktuálním elementu.
		/// </summary>
		public bool EnabledStateDependingOnElement { get; set; }
		/// <summary> { get; set; }
		/// Příznak, že položka menu má mít sub-položky, viz this.MenuSubItemsCaption
		/// </summary>
		public bool MenuHasSubItems { get; set; }
		/// <summary> { get; set; }
		/// Text menu podpoložek menu.
		/// Klíč v Dictionary je klíčem podpoložky.
		/// Value v Dictionary je zobrazovaný text.
		/// Podpoložky mají Images v MenuSubItemsImage.
		/// </summary>
		public Dictionary<string, string> MenuSubItemsCaption { get; set; }
		/// <summary> { get; set; }
		/// Text menu podpoložek menu.
		/// Klíč v Dictionary je klíčem podpoložky.
		/// Value v Dictionary je Image pro položku menu.
		/// </summary>
		public Dictionary<string, Image> MenuSubItemsImage { get; set; }
		/// <summary> { get; set; }
		/// Skupina menu. Pokud chceme mít svoje položky menu pohromadě, pak sem vložíme unikátní název skupiny. 
		/// Skupiny budou setříděny podle tohoto klíče skupiny, vzestupně. Klíč se nikde nezobrazuje. 
		/// Mezi položkami menu s různými klíči je vykreslen separátor.
		/// Hodnotou MenuOrder zajistíme třídění položek v rámci skupiny.
		/// </summary>
		public string MenuGroup { get; set; }
		/// <summary> { get; set; }
		/// Pořadí položky menu v rámci její skupiny MenuGroup.
		/// Výchozí hodnota je 0.
		/// Funkce může vepsat hodnotu zápornou (pak bude funkce uvedena v nabídce víc nahoře) nebo kladnou (pak bude níže).
		/// Funkce se stejnou hodnotou budou tříděny abecedně.
		/// </summary>
		public int MenuOrder { get; set; }
		#endregion
		#region PROMĚNNÉ
		private FunctionMenuItemKey _MenuKey;
		#endregion
	}
	#endregion
	#region CLASS FunctionMenuItemRunArgs : ARGUMENTY PRO BĚH FUNKCE (In/Out data)
	/// <summary>
	/// Třída argumentů pro běh funkce (vstupní a výstupní data z běhu funkce).
	/// </summary>
	public class FunctionMenuItemRunArgs
	{
		#region KONSTRUKCE
		/// <summary>
		/// Konstruktor
		/// </summary>
		/// <param name="graphMode">Typ grafu</param>
		/// <param name="firstRowGId">Obsahuje GID dat, která se načetla do grafu jako základní seznam řádků.</param>
		/// <param name="tag">Tag předaný v deklaraci grafu, který volá tuto funkci.</param>
		/// <param name="menuKind">Prostor, kde se kliklo</param>
		/// <param name="elementShapeType">Druh (tvar) elementu, na který se kliknulo</param>
		/// <param name="clickedItem">Pointer na element (GID řádku a elementu)</param>
		/// <param name="dataSource">Odkaz na datový zdroj</param>
        public FunctionMenuItemRunArgs(RowGraphMode graphMode, GID firstRowGId, object tag, FunctionMenuItemAreaType menuKind, GraphElementShapeType elementShapeType, DataPointerStr clickedItem, IDataSource dataSource, FindDataElementHandler findDataElement)
		{
			this._GraphMode = graphMode;
			this._FirstRowGId = firstRowGId;
			this._Tag = tag;
			this._AreaType = menuKind;
			this._ElementShapeType = elementShapeType;
			this._ClickedItem = clickedItem;
			this._SelectedElements = null;
			this._DataSource = dataSource;
            this._FindDataElement = findDataElement;
            this._ResultEditNewDataRows = new List<IDataRow>();
            this._ResultEditNewRows = new List<GID>();
			this._ResultEditChangedRows = new List<GID>();
            this._ResultEditDeletedRows = new List<GID>();
            this._ResultRowFilterList = new List<GID>();
            this._ResultReloadAllRows = false;
            this._ResultSetTimeRange = TimeRange.Empty;
			this._ResultTreeNodeAction = GridTreeNodeActionType.None;
		}
		#endregion
		#region PROMĚNNÉ
		private RowGraphMode _GraphMode;
		private GID _FirstRowGId;
		private object _Tag;
		private FunctionMenuItemAreaType _AreaType;
		private GraphElementShapeType _ElementShapeType;
		private DataPointerStr _ClickedItem;
		private List<DataPointerStr> _SelectedElements;
		private List<DataPointerStr> _InputElements;
		private IDataSource _DataSource;
        private List<IDataRow> _ResultEditNewDataRows;
        private List<GID> _ResultEditNewRows;
        private List<GID> _ResultEditChangedRows;
        private List<GID> _ResultEditDeletedRows;
        private List<GID> _ResultRowFilterList;
        private bool _ResultReloadAllRows;
        private TimeRange _ResultSetTimeRange;
		private bool _ResultElementLinkListChanged;
		private GridTreeNodeActionType _ResultTreeNodeAction;
		#endregion
		#region Public property
		/// <summary> { get; }
		/// Typ grafu v aktuálním řádku
		/// </summary>
		public RowGraphMode GraphMode { get { return this._GraphMode; } }
		/// <summary>
		/// Obsahuje GID dat, která se načetla do grafu jako základní seznam řádků. Typicky určuje třídu dat v seznamu / v nejvyšší úrovni stromu.
		/// </summary>
		public GID FirstRowGId { get { return this._FirstRowGId; } }
		/// <summary>
		/// Tag předaný v deklaraci grafu, který volá tuto funkci.
		/// </summary>
		public object Tag { get { return this._Tag; } }
		/// <summary> { get; }
		/// Typ prostoru, kam uživatel kliknul
		/// </summary>
		public FunctionMenuItemAreaType AreaType { get { return this._AreaType; } }
		/// <summary> { get; }
		/// Pointer na řádek a element, na kterém došlo ke kliknutí
		/// </summary>
		public DataPointerStr ClickedItem { get { return this._ClickedItem; } }
		/// <summary> { get; }
		/// Tvar elementu.
		/// </summary>
		public GraphElementShapeType ElementShapeType { get { return this._ElementShapeType; } }
		/// <summary>
		/// Datový zdroj grafu
		/// </summary>
		public IDataSource DataSource { get { return this._DataSource; } }
        /// <summary>
        /// Najde a vrátí data elementu pro daný pointer
        /// </summary>
        public IDataElement FindElement(DataPointerStr pointer)
        {
            if (this._FindDataElement == null) return null;
            return this._FindDataElement(pointer);
        }
        protected FindDataElementHandler _FindDataElement;
		/// <summary>
		/// Seznam aktuálně vybraných elementů. Nikdy není null.
		/// </summary>
		public List<DataPointerStr> SelectedElements
		{
			get
			{
				if (this._SelectedElements == null)
					this._SelectedElements = new List<DataPointerStr>();
				return this._SelectedElements;
			}
			set { this._SelectedElements = value; this._InputElements = null; }
		}
		/// <summary>
		/// Seznam vstupních elementů: jsou to elementy selectované (označené) plus element, na který uživatel kliknul (ten je vždy první v seznamu).
		/// Pokud uživatel kliknul mimo element, a nic nevybral, je zde prázdný seznam.
		/// </summary>
		public List<DataPointerStr> InputElements
		{
			get
			{
				if (this._InputElements == null)
				{
					DataPointerStr click = this._ClickedItem;

					// Seznam unikátních pointerů selectovaných elementů:
					Dictionary<DataPointerStr, object> dict = new Dictionary<DataPointerStr, object>();
					if (this._SelectedElements != null)
					{
						foreach (DataPointerStr pointer in this._SelectedElements)
						{
							if (!pointer.IsEmpty && pointer != click && !dict.ContainsKey(pointer))
								dict.Add(pointer, null);
						}
					}

					// Výsledný soupis:
					List<DataPointerStr> list = new List<DataPointerStr>();
					if (!click.IsEmpty)
						list.Add(click);
					if (dict.Count > 0)
						list.AddRange(dict.Keys);

					this._InputElements = list;
				}
				return this._InputElements;
			}
		}
        /// <summary>
        /// Obsahuje true, pokud evidujeme změněné řádky (ResultEditChangedRows není null a obsahuje alespoň jednu položku).
        /// </summary>
        public bool ResultContainChangedRows { get { return (this._ResultEditChangedRows != null && this._ResultEditChangedRows.Count > 0); } }

        /// <summary>
        /// Výsledek funkce: je požadováno načtení uvedených řádků. Vlivem funkce byly založeny jako nové.
        /// Každý graf o ně požádá prostřednictvím svého datového zdroje, datový zdroj by měl poznat zda jsou jeho.
        /// </summary>
        public List<GID> ResultEditNewRows { get { return this._ResultEditNewRows; } }
        /// <summary>
        /// Výsledek funkce: tyto řádky byly založeny během běhu funkce.
        /// Každý graf je dostane k dispozici, a zařadí je tehdy, pokud odpovídá typ grafu uvedený v řádku k typu grafu,
        /// a pokud: a) řádek obsahuje ParentGId konkrétního a existujícího řádku v daném grafu,
        /// anebo b) řádek obsahuje ParentGId bez čísla recordu a s číslem třídy odpovídajícím grafu.
        /// </summary>
        public List<IDataRow> ResultEditNewDataRows { get { return this._ResultEditNewDataRows; } }
		/// <summary>
		/// Výsledek funkce: je požadována invalidace těchto řádků (ať jsou na kterémkoli grafu).
		/// Invalidace zajistí znovunačtení jejich elementů, ale ne řádku jako takového (tzn. texty v záhlaví řádku se nezmění).
        /// Pokud má řádek nějaké podřízené řádky, budou zahozeny a načteny znovu (jako by se poprvé otevřel Tree s podřízenými řádky).
        /// Pokud řádek zahazuje svoje podřízené řádky, které již měl načteny, pak si nové řádky donačte automaticky ihned (aby uživateli nezmizely z očí).
        /// Nové řádky přitom mohou být v seznamu this.ResultNewRows: pokud tam pro invalidovaný řádek existuje alespoň jeden podřízený řádek, pak se nebude provádět donačtení přes datový zdroj.
		/// Po znovunačtení řádků dojde automaticky k refreshování vizuální vrstvy = překreslení grafů.
		/// </summary>
		public List<GID> ResultEditChangedRows { get { return this._ResultEditChangedRows; } }
        /// <summary>
        /// Výsledek funkce: je požadováno odstranění těchto řádků (ať jsou na kterémkoli grafu).
        /// Funkce odpovídající data smazala.
        /// </summary>
        public List<GID> ResultEditDeletedRows { get { return this._ResultEditDeletedRows; } }
        /// <summary>
        /// Výsledek funkce: Filtr na řádky v this grafu.
        /// </summary>
        public List<GID> ResultRowFilterList { get { return this._ResultRowFilterList; } }
		/// <summary>
		/// Příznak, že důsledkem editace došlo ke změnám všech řádků, nebo k přidání/odebrání řádků.
		/// Pak je třeba, aby došlo k přenačtení všech řádků.
		/// </summary>
		public bool ResultReloadAllRows { get { return this._ResultReloadAllRows; } set { this._ResultReloadAllRows = value; } }
		/// <summary>
		/// Výsledek funkce: je třeba nastavit časovou osu tak, aby zobrazovala uvedený čas.
        /// Výchozí hodnota je TimeRange.Empty = časová osa se nemění.
		/// </summary>
		public TimeRange ResultSetTimeRange { get { return this._ResultSetTimeRange; } set { this._ResultSetTimeRange = value; } }
		/// <summary>
		/// Příznak, že důsledkem editace došlo ke změnám v seznamu linkovaných elementů.
		/// Poté by měla vizuální vrstva znovu načíst seznam linků elementů (požadavkem typu DataSourceRequestReadRelations.CreateLinkNet())
		/// a zobrazovat jej z tohoto nově získaného seznamu.
		/// </summary>
		public bool ResultElementLinkListChanged { get { return this._ResultElementLinkListChanged; }  set { this._ResultElementLinkListChanged = value; } }
		/// <summary>
		/// Odpověď funkce, která řídí rozbalování/sbalování nodů v seznamu.
		/// Default = None.
		/// </summary>
		public GridTreeNodeActionType ResultTreeNodeAction { get { return this._ResultTreeNodeAction; } set { this._ResultTreeNodeAction = value; } }
		#endregion
	}
	#endregion
	#region ENUM FunctionMenuItemAreaType : místo v grafu, kam člověk kliknul
	/// <summary>
	/// Druh objektu, na který uživatel kliknul
	/// </summary>
	public enum FunctionMenuItemAreaType
	{
		None = 1,
		/// <summary>
		/// Ikonka Tree:PLUS (rozbalit zabalené uzly stromu)
		/// </summary>
		TreeIconExpand,
		/// <summary>
		/// Ikonka Tree:MINUS (zabalit rozbalené uzly stromu)
		/// </summary>
		TreeIconCollapse,
		/// <summary>
		/// Prázdný řádek v grafu mezi elementy
		/// </summary>
		EmptyRow,
		/// <summary>
		/// Textová část grafu, některý sloupec
		/// </summary>
		TextItem,
		/// <summary>
		/// Záhlaví řádku (vlevo v řádku)
		/// </summary>
		RowHeader,
		/// <summary>
		/// Záhlaví sloupce (nahoře)
		/// </summary>
		ColumnHeader,
		/// <summary>
		/// Prvek grafu, element
		/// </summary>
		GraphElement
	}
	/// <summary>
	/// Požadovaná akce grafu typu Tree: rozbalit / zabalit uzly (hromadně)
	/// </summary>
	public enum GridTreeNodeActionType
	{
		/// <summary>
		/// Žádná akce
		/// </summary>
		None = 1,
		/// <summary>
		/// Rozbalit všechny nody
		/// </summary>
		ExpandAll,
		/// <summary>
		/// Zabalit všechny nody
		/// </summary>
		CollapseAll,
		/// <summary>
		/// Zabalit všechny nody kromě aktivního
		/// </summary>
		CollapseAllButThis
	}
	#endregion
}
